package sync

import (
	"context"
	"fmt"
	"gitee.com/hexug/go-tools/ioc"
	"gitee.com/hexug/go-tools/logger"
	"gitee.com/hexug/kube-sync/apps/cleandata"
	"gitee.com/hexug/kube-sync/apps/resource"
	"gitee.com/hexug/kube-sync/apps/tofile"
	yamlv3 "gopkg.in/yaml.v3"
	"k8s.io/client-go/kubernetes"
	"path/filepath"
	"strings"
)

// WorkloadSyncer 封装实际工作逻辑
type WorkloadSyncer struct {
	ctx        context.Context
	namespaces []string
	excludes   []string
	includes   []string
	outpath    string

	clientSet *kubernetes.Clientset

	dpsvc   resource.Server
	clndsvc cleandata.Server
	tfsvc   tofile.Server

	skipConfigMap bool
	skipPvPvc     bool
	skipSecret    bool
}

// NewWorkloadSyncer 构造器
func NewWorkloadSyncer(
	ctx context.Context,
	namespaces,
	excludes []string,
	includes []string,
	outpath string,
	clientSet *kubernetes.Clientset,
	skipConfigMap bool,
	skipPvPvc bool,
	skipSecret bool,
) *WorkloadSyncer {
	return &WorkloadSyncer{
		ctx:        ctx,
		namespaces: namespaces,
		excludes:   excludes,
		includes:   includes,
		outpath:    outpath,

		clientSet: clientSet,

		dpsvc:   ioc.Container.GetApp(resource.AppName).(resource.Server),
		clndsvc: ioc.Container.GetApp(cleandata.AppName).(cleandata.Server),
		tfsvc:   ioc.Container.GetApp(tofile.AppName).(tofile.Server),

		skipConfigMap: skipConfigMap,
		skipPvPvc:     skipPvPvc,
		skipSecret:    skipSecret,
	}
}

type requestWriteToFile struct {
	resDep    *cleandata.CleanWorkloadResponse
	resSvc    *cleandata.CleanServiceResponse
	resCM     *cleandata.CleanConfigMapResponse
	resRBAC   *cleandata.CleanRBACResponse
	resPvPvc  *cleandata.CleanPvPvcResponse
	resSecret *cleandata.CleanSecretResponse
}

// Run 主流程
func (s *WorkloadSyncer) Run() {
	req := s.buildRequest()
	dps, err := s.dpsvc.ListWorkload(s.ctx, req)
	if err != nil {
		logger.L().Fatal(err)
	}
	wks := []interface{}{}
	for _, item := range dps.Items {
		wks = append(wks, item.Workload)
	}
	resDep := s.cleanWorkloads(dps)
	resSvc := s.cleanServices(wks, s.clientSet)
	var resCM *cleandata.CleanConfigMapResponse
	if !s.skipConfigMap {
		resCM = s.cleanConfigMap(wks, s.clientSet)
	}
	resRBAC := s.CleanRBAC(wks, s.clientSet)
	var resPvPvc *cleandata.CleanPvPvcResponse
	if !s.skipPvPvc {
		resPvPvc = s.CleanPvPvc(wks, s.clientSet)
	}
	var resSecret *cleandata.CleanSecretResponse
	if !s.skipSecret {
		resSecret = s.CleanSecret(wks, s.clientSet)
	}
	reqWtf := &requestWriteToFile{resDep, resSvc, resCM, resRBAC, resPvPvc, resSecret}
	s.writeToFile(reqWtf)
}

// ----------------- 内部方法 -----------------
//
//func (s *WorkloadSyncer) buildDeploymentRequest() *BuildRequest {
//	req := NewBuildRequest()
//	depReq := resource.NewListDeployRequest()
//	depReq.ClientSet = s.clientSet
//
//	if len(s.namespaces) == 0 {
//		logger.L().Fatal("需要指定命名空间参数")
//	}
//	for _, ns := range s.namespaces {
//		if ns == "" {
//			logger.L().Fatalf("namespaces %s 参数不合法", ns)
//		}
//		depReq.AddNamespaces(ns)
//	}
//
//	for _, val := range s.excludes {
//		parts := strings.Split(val, ":")
//		if len(parts) != 3 || parts[1] == "" || parts[2] == "" {
//			logger.L().Fatalf("excludes %s 参数不合法", val)
//		}
//		switch parts[1] {
//		case "Deployment":
//			depReq.AddExcludeDeployName(parts[0], parts[2])
//		default:
//			logger.L().Fatalf("资源类型 %s 尚未实现", parts[1])
//		}
//	}
//
//	return req
//}

func (s *WorkloadSyncer) buildRequest() *resource.ListWorkloadRequest {
	woekloadReq := resource.NewListWorkloadRequest()
	woekloadReq.ClientSet = s.clientSet

	if len(s.namespaces) == 0 {
		logger.L().Fatal("需要指定命名空间参数")
	}
	for _, ns := range s.namespaces {
		if ns == "" {
			logger.L().Fatalf("namespaces %s 参数不合法", ns)
		}
		woekloadReq.AddNamespaces(ns)
	}

	if len(s.excludes) > 0 {
		for _, val := range s.excludes {
			parts := strings.Split(val, ":")
			if len(parts) != 3 || parts[1] == "" || parts[2] == "" {
				logger.L().Fatalf("excludes %s 参数不合法", val)
			}
			wcore := resource.NewDefaultExcludeWorkloadCore(parts[0], parts[2], parts[1])
			woekloadReq.AddExcludeWorkload(wcore)
		}
	} else if len(s.includes) > 0 {
		for _, val := range s.includes {
			parts := strings.Split(val, ":")
			if len(parts) != 3 || parts[1] == "" || parts[2] == "" {
				logger.L().Fatalf("includes %s 参数不合法", val)
			}
			wcore := resource.NewDefaultExcludeWorkloadCore(parts[0], parts[2], parts[1])
			woekloadReq.AddIncludeWorkload(wcore)
		}
	}

	return woekloadReq
}

func (s *WorkloadSyncer) writeToFile(req *requestWriteToFile) {
	for key, work := range req.resDep.Items {
		parts := strings.Split(key, ":")
		if len(parts) != 3 || parts[1] == "" || parts[2] == "" {
			logger.L().Fatalf("key %s 参数不合法", key)
		}
		docs := [][]byte{}

		// Deployment
		data, err := yamlv3.Marshal(work.Data)
		if err != nil {
			logger.L().Fatal(err)
		}
		docs = append(docs, data)

		// Service
		for nsKey, svcs := range req.resSvc.Items {
			if nsKey == key {
				for _, svc := range svcs {
					data, err = yamlv3.Marshal(svc.Data)
					if err != nil {
						logger.L().Fatal(err)
					}
					docs = append(docs, data)
				}
			}
		}

		// 文件路径
		path := filepath.Join(s.outpath, fmt.Sprintf("%s/%s-%s/%s.yaml", parts[0], parts[1], parts[2], parts[2]))

		reqtw := tofile.NewWriterYamlRequest()
		reqtw.Add(path, docs)

		err = s.tfsvc.WriterYaml(s.ctx, reqtw)
		if err != nil {
			logger.L().Fatal(err)
		}
		logger.L().Debugf("write %s to: %s", parts[1], path)
	}
	if req.resCM != nil {
		// 处理ConfigMap
		for ns_type_wn, cms := range req.resCM.Items {
			nss := strings.Split(ns_type_wn, ":")
			if len(nss) != 3 {
				logger.L().Fatalf("configmap 这里的 key %s 格式错误", ns_type_wn)
			}
			for _, cm := range cms {
				// 文件路径
				path := filepath.Join(s.outpath, fmt.Sprintf("%s/%s-%s/configmap-%s.yaml", cm.Namespace, nss[1], cm.WorkloadName, cm.Name))
				data, err := yamlv3.Marshal(cm.Data)
				if err != nil {
					logger.L().Fatal(err)
				}
				reqtw := tofile.NewWriterYamlRequest()
				reqtw.Add(path, [][]byte{data})
				err = s.tfsvc.WriterYaml(s.ctx, reqtw)
				if err != nil {
					logger.L().Fatal(err)
				}
				logger.L().Debugf("write configmap to: %s", path)
			}
		}
	}

	// 处理RBAC
	for key, rbacInfo := range req.resRBAC.Items {
		docs := [][]byte{}
		nss := strings.Split(key, ":")
		if len(nss) != 3 {
			logger.L().Fatalf("rbac 这里的 key %s 格式错误", key)
		}
		sa, err := yamlv3.Marshal(rbacInfo.ServiceAccount)
		if err != nil {
			logger.L().Fatal(err)
		}
		docs = append(docs, sa)
		for _, crb := range rbacInfo.ClusterRBs {
			crbs, err := yamlv3.Marshal(crb)
			if err != nil {
				logger.L().Fatal(err)
			}
			docs = append(docs, crbs)
		}
		for _, rb := range rbacInfo.RoleBindings {
			rbs, err := yamlv3.Marshal(rb)
			if err != nil {
				logger.L().Fatal(err)
			}
			docs = append(docs, rbs)
		}
		for _, r := range rbacInfo.Roles {
			rs, err := yamlv3.Marshal(r)
			if err != nil {
				logger.L().Fatal(err)
			}
			docs = append(docs, rs)
		}
		for _, cr := range rbacInfo.ClusterRoles {
			crs, err := yamlv3.Marshal(cr)
			if err != nil {
				logger.L().Fatal(err)
			}
			docs = append(docs, crs)
		}
		// 文件路径
		path := filepath.Join(s.outpath, fmt.Sprintf("%s/%s-%s/rbac.yaml", nss[0], nss[1], nss[2]))

		reqtw := tofile.NewWriterYamlRequest()
		reqtw.Add(path, docs)

		err = s.tfsvc.WriterYaml(s.ctx, reqtw)
		if err != nil {
			logger.L().Fatal(err)
		}
		logger.L().Debugf("write rbac to: %s", path)
	}

	if req.resPvPvc != nil {
		for key, pvPvcInfo := range req.resPvPvc.Items {
			docs := [][]byte{}
			nss := strings.Split(key, ":")
			if len(nss) != 3 {
				logger.L().Fatalf("rbac 这里的 key %s 格式错误", key)
			}
			for _, pv := range pvPvcInfo.PVs {
				pvs, err := yamlv3.Marshal(pv)
				if err != nil {
					logger.L().Fatal(err)
				}
				docs = append(docs, pvs)
			}
			for _, pvc := range pvPvcInfo.PVCs {
				pvcs, err := yamlv3.Marshal(pvc)
				if err != nil {
					logger.L().Fatal(err)
				}
				docs = append(docs, pvcs)
			}
			// 文件路径
			path := filepath.Join(s.outpath, fmt.Sprintf("%s/%s-%s/pv-pvc.yaml", nss[0], nss[1], nss[2]))
			reqtw := tofile.NewWriterYamlRequest()
			reqtw.Add(path, docs)
			err := s.tfsvc.WriterYaml(s.ctx, reqtw)
			if err != nil {
				logger.L().Fatal(err)
			}
			logger.L().Debugf("write pv-pvc to: %s", path)
		}
	}
	if req.resSecret != nil {
		// 处理Secret
		for key, secrets := range req.resSecret.Items {
			docs := [][]byte{}
			nss := strings.Split(key, ":")
			if len(nss) != 3 {
				logger.L().Fatalf("rbac 这里的 key %s 格式错误", key)
			}
			for _, secret := range secrets.Secrets {
				s, err := yamlv3.Marshal(secret)
				if err != nil {
					logger.L().Fatal(err)
				}
				docs = append(docs, s)
			}
			// 文件路径
			path := filepath.Join(s.outpath, fmt.Sprintf("%s/%s-%s/secret.yaml", nss[0], nss[1], nss[2]))
			reqtw := tofile.NewWriterYamlRequest()
			reqtw.Add(path, docs)
			err := s.tfsvc.WriterYaml(s.ctx, reqtw)
			if err != nil {
				logger.L().Fatal(err)
			}
			logger.L().Debugf("write Secret to: %s", path)
		}
	}
}
